package czk.jvm.lecture.classloader;

import java.io.*;

/**
 * 自定义类加载器
 * 加载字节码文件，通过反射创建对象
 */
public class MyTest16 extends ClassLoader {
    //扩展名
    public  final String extensionName=".class";
    //类加载器名称
    public String classLoaderName;
    //定义加载路径
    public String path;

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 双亲委派机制，此时默认的父加载器为系统类加载器
     * @param classLoaderName
     */
    public MyTest16(String classLoaderName){
        super();
        this.classLoaderName=classLoaderName;
    }

    /**
     * 传入父类加载器，此时父加载器为制定的父加载器
     * @param parent
     * @param classLoaderName
     */
    public MyTest16(ClassLoader parent,String classLoaderName){
        super(parent);
        this.classLoaderName=classLoaderName;
    }

    /**
     * 指定其父加载器
     * 将其委托为系统类加载器必备,如果不写，在将其指为系统类加载器时会报错：
     * Error occurred during initialization of VM
     * @param classLoader
     */
    public MyTest16(ClassLoader classLoader){
        super(classLoader);
    }
    /**
     *
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = loadClassData(name);

        return this.defineClass(name,data,0,data.length);//off 偏移的字节长度，data。length写入的字节
    }

    /**
     * 加载类
     * @param name
     * @return
     */
    private byte[] loadClassData(String name){
        byte[] data=null;
        ByteArrayOutputStream byteArrayInputStream=null;
        InputStream inputStream=null;
        try {
            this.classLoaderName=this.classLoaderName.replaceAll(".","/");
            //转义字符
            name=name.replaceAll("\\.","\\\\");
            inputStream=new FileInputStream(new File(this.path+"\\"+name+this.extensionName));
            byteArrayInputStream =new ByteArrayOutputStream();
            int len;
            while ((len=inputStream.read())!=-1){
                byteArrayInputStream.write(len);
            }
            data=byteArrayInputStream.toByteArray();
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            try {
                byteArrayInputStream.close();
                inputStream.close();
            }catch (IOException ex){
                ex.printStackTrace();
            }
        }
        return data;
    }
    public static void test(ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //由于双亲委派机制，加载该类的系统类加载器，加载工程下的字节码文件
        Class<?> aClass = classLoader.loadClass("czk.jvm.lecture.classloader.MyTest12");
        Object object = aClass.newInstance();
        System.out.println(object);
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException, InterruptedException {
        MyTest16 loader1=new MyTest16("loader1");
        //加载E盘下的文件，appclassloader只加载工程下的文件，所以父亲加载器不能加载，由自定义的类加载器加载
        loader1.setPath("E:");
        Class<?> aClass = loader1.loadClass("czk.jvm.lecture.classloader.MyTest4");
        System.out.println(aClass.hashCode());
        Object object = aClass.newInstance();
        System.out.println(object);
        System.out.println("======");
        System.out.println(aClass.getClassLoader());
        System.out.println(aClass.getClassLoader().getParent());
        //两个不同的类加载器的实例具有不同的命名空间，所以加载出来的类也是不一样的
        //如果重新构建字节码文件，由双亲委派机制的父类加载器加载文件，则是同一命名空间，hashcode值相同
        MyTest16 loader2=new MyTest16("loader2");
        //加载E盘下的文件，appclassloader只加载工程下的文件，所以父亲加载器不能加载，由自定义的类加载器加载
        loader2.setPath("E:");
        Class<?> aClass1 = loader2.loadClass("czk.jvm.lecture.classloader.MyTest4");
        System.out.println(aClass1.getClassLoader());
        System.out.println(aClass1.hashCode());
        Object object1 = aClass.newInstance();
        System.out.println(object1);
        System.out.println("======");
        // 父；类加载器是app类加载器
        System.out.println(aClass1.getClassLoader().getParent());
        //如果重新构建字节码文件，由双亲委派机制的父类加载器加载文件，则是同一命名空间，hashcode值相同
        System.out.println(object.equals(object1));
    }
}
